// 2010-02-25 - M. Caputo

#include "GlWidget.h"
#include "WireframeMode.h"
#include "DotMode.h"
#include "FlatShadedMode.h"

GlWidget::GlWidget(QWidget *parent): QGLWidget(parent)
{
	scene = 0;
	
	renderModes.append(new WireframeMode());
	renderModes.append(new DotMode());
	renderModes.append(new FlatShadedMode());
	activeMode = 0;

	ctrlDown = false;
	altDown = false;

	setFocusPolicy(Qt::ClickFocus);
	
	resetView();
}

GlWidget::~GlWidget()
{
	for(int i=0; i<renderModes.size(); i++) {
		delete renderModes[i];
	}
}

void GlWidget::initializeGL()
{
	glClearColor(
		bgColor.redF(),
		bgColor.greenF(),
		bgColor.blueF(),
		bgColor.alphaF()
	);

	renderModes[activeMode]->setSettings();
}

void GlWidget::resizeGL(int w, int h)
{
	int side = qMin(w, h);
    glViewport((w - side) / 2, (h - side) / 2, side, side);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
	glFrustum(-1.0, +1.0, -1.0, +1.0, 1.5, 20.0);
	glMatrixMode(GL_MODELVIEW);
}

void GlWidget::paintGL()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	
	glTranslatef(0.0, 0.0, -3.0);
	
	glScalef(scale, scale, scale);
	
	glTranslatef(xTrans, yTrans, zTrans);
	
	glRotatef(xRot/2.0, 1.0, 0.0, 0.0);
	glRotatef(yRot/2.0, 0.0, 1.0, 0.0);
	glRotatef(zRot/2.0, 0.0, 0.0, 1.0);
	
	if (showAxes) drawAxes();
	
	int lines = 5;
	float stepSize = 0.2;
	QColor lineColor(100, 100, 100); // grey
	
	if (showPlanes[0]) drawXzPlane(lines, stepSize, lineColor);
	if (showPlanes[1]) drawXyPlane(lines, stepSize, lineColor);
	if (showPlanes[2]) drawYzPlane(lines, stepSize, lineColor);
	
	if (scene) {
		renderModes[activeMode]->draw(scene, color);
	}
}

void GlWidget::mousePressEvent(QMouseEvent *event)
{
	lastPos = event->pos();
}

void GlWidget::mouseMoveEvent(QMouseEvent *event)
{
	int dx = event->x() - lastPos.x();
	int dy = event->y() - lastPos.y();

	if (event->buttons() & Qt::LeftButton) {
		xRot += dy;
		yRot += dx;
	} /*else if (event->buttons() & Qt::RightButton) {
		xRot += dy;
		zRot += dx;
	}*/

	//fuer translatef
	if ((event->buttons() & Qt::RightButton) && !ctrlDown && !altDown) {
		xTrans += (static_cast<double>(dx)/200.0);
		yTrans += -(static_cast<double>(dy)/200.0);
	} else if ((event->buttons() & Qt::RightButton) && ctrlDown && !altDown) {
		zTrans += (static_cast<double>(dy)/200.0);
	}
	
	lastPos = event->pos();
	updateGL();
}

void GlWidget::wheelEvent(QWheelEvent *event)
{
	if (event->delta() > 0) {
		scale += 0.1;
	} else {
		if (scale > 0.1) scale -= 0.1;
	}

	updateGL();
}

void GlWidget::keyPressEvent(QKeyEvent *event)
{
	switch(event->key()) {
		case Qt::Key_Y :
			ctrlDown = true;
			break;
		case Qt::Key_X :
			altDown = true;
			break;
		default :
			QGLWidget::keyPressEvent(event);
	}
}

void GlWidget::keyReleaseEvent(QKeyEvent *event)
{
	switch(event->key()) {
		case Qt::Key_Y :
			if(!event->isAutoRepeat()) {
				ctrlDown = false;
			}
			break;
		case Qt::Key_X :
			if(!event->isAutoRepeat()) {
				altDown = false;
			}
			break;
		default :
			QGLWidget::keyReleaseEvent(event);
	}
}

void GlWidget::drawAxes()
{
	glLineWidth(3.0);
	glBegin( GL_LINES );	
		glColor3f(1, 0, 0);
		glVertex3f(0, 0, 0);
		glVertex3f(1, 0, 0);
		
		glColor3f(0, 1, 0);
		glVertex3f(0, 0, 0);
		glVertex3f(0, 1, 0);

		glColor3f(0, 0, 1);
		glVertex3f(0, 0, 0);
		glVertex3f(0, 0, 1);	
	glEnd();
	glLineWidth(1.0);
}

void GlWidget::drawXzPlane(int lines, float stepSize, const QColor & c)
{
	glColor3f(c.redF(), c.greenF(), c.blueF());
	glBegin( GL_LINES );
	for(int i=-lines; i<=lines; i++) {
		glVertex3f(stepSize*i, 0, -lines*stepSize);
		glVertex3f(stepSize*i, 0, lines*stepSize);
	}
	for(int i=-lines; i<=lines; i++) {
		glVertex3f(-lines*stepSize, 0, stepSize*i);
		glVertex3f(lines*stepSize, 0, stepSize*i);
	}	
	glEnd();
}

void GlWidget::drawXyPlane(int lines, float stepSize, const QColor & c)
{
	glColor3f(c.redF(), c.greenF(), c.blueF());
	glBegin( GL_LINES );
	for(int i=-lines; i<=lines; i++) {
		glVertex3f(stepSize*i, -lines*stepSize, 0);
		glVertex3f(stepSize*i, lines*stepSize, 0);
	}
	for(int i=-lines; i<=lines; i++) {
		glVertex3f(-lines*stepSize, stepSize*i, 0);
		glVertex3f(lines*stepSize, stepSize*i, 0);
	}	
	glEnd();
}

void GlWidget::drawYzPlane(int lines, float stepSize, const QColor & c)
{
	glColor3f(c.redF(), c.greenF(), c.blueF());
	glBegin( GL_LINES );
	for(int i=-lines; i<=lines; i++) {
		glVertex3f(0, stepSize*i, -lines*stepSize);
		glVertex3f(0, stepSize*i, lines*stepSize);
	}
	for(int i=-lines; i<=lines; i++) {
		glVertex3f(0, -lines*stepSize, stepSize*i);
		glVertex3f(0, lines*stepSize, stepSize*i);
	}	
	glEnd();
}

void GlWidget::setScene(IScene *scene)
{
	this->scene = scene;
	updateGL();
}

void GlWidget::setBackgroundColor(const QColor & c)
{
	bgColor = c;
	updateGL();
}

QColor GlWidget::backgroundColor()
{
	return bgColor;
}

void GlWidget::setObjectColor(const QColor & c)
{
	color = c;
	updateGL();
}

QColor GlWidget::objectColor()
{
	return color;
}

void GlWidget::setXzPlane(bool status)
{
	showPlanes[0] = status;
	updateGL();
}

bool GlWidget::xzPlane()
{
	return showPlanes[0];
}

void GlWidget::setXyPlane(bool status)
{
	showPlanes[1] = status;
	updateGL();
}

bool GlWidget::xyPlane()
{
	return showPlanes[1];
}

void GlWidget::setYzPlane(bool status)
{
	showPlanes[2] = status;
	updateGL();
}

bool GlWidget::yzPlane()
{
	return showPlanes[2];
}

void GlWidget::setAxes(bool status)
{
	showAxes = status;
	updateGL();
}

bool GlWidget::axes()
{
	return showAxes;
}

void GlWidget::resetView()
{
	color = QColor("white");
	bgColor = QColor("black");
	
	showAxes = true;
	
	showPlanes[0] = true;
	showPlanes[1] = false;
	showPlanes[2] = false;

	scale = 1.0;
	
	xRot = 0;
	yRot = 0;
	zRot = 0;
	
	xTrans = 0;
	yTrans = 0;
	zTrans = 0;
	
	updateGL();
}

QStringList GlWidget::listRenderModes()
{
	QStringList list;
	for(int i=0; i<renderModes.size(); i++) {
		list.append(renderModes[i]->name());
	}
	return list;
}

int GlWidget::renderMode()
{
	return activeMode;
}

void GlWidget::setRenderMode(int m)
{
	renderModes[activeMode]->unsetSettings();
	activeMode = m;
	renderModes[activeMode]->setSettings();
	updateGL();
}

QString GlWidget::renderModeName()
{
	return renderModes[activeMode]->name();
}
